package com.edu.learning.concurrent.sample.mode1;

import java.time.Duration;
import java.time.LocalTime;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.*;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * @author ll
 * @Date 2017/10/26 15:52
 */
public class ConcurrentLinkedQueueExam {
    private static final int TEST_COUNT = 10000000;

    public static void main(String[] args) throws InterruptedException {
        LocalTime start = LocalTime.now();
        Queue<Integer> queue = null;
        if (args.length < 1) {
            System.out.println("Usage: input 1~4 ");
            System.exit(1);
        }
        int param = Integer.parseInt(args[0]);
        switch (param) {
            case 1:
                queue = new LinkedList<>();
                break;
            case 2:
                queue = new LinkedBlockingQueue<>();
                break;
            case 3:
                queue = new ArrayBlockingQueue<Integer>(TEST_COUNT * 5);
                break;
            case 4:
                queue = new ConcurrentLinkedQueue<>();
                break;
            default:
                System.out.println("Usage：input 1~4 ");
                System.out.println(2);
        }
        System.out.println("Using " + queue.getClass().getSimpleName());

        ExecutorService executorService = new ThreadPoolExecutor(
                32,
                32,
                0,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 5; i ++) {
            executorService.submit(new Putter(queue, "Putter " + i));
        }
        SECONDS.sleep(2);
        for (int i = 0; i < 5; i ++) {
            executorService.submit(new Getter(queue, "Getter " + i));
        }
        executorService.awaitTermination(1, TimeUnit.DAYS);
        LocalTime end = LocalTime.now();
        System.out.println("Time span = " + Duration.between(end, start));
    }

    static class Putter implements Runnable {
        private final Queue<Integer> queue;
        private final String name;

        public Putter(Queue<Integer> queue, String name) {
            this.queue = queue;
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i < TEST_COUNT; i ++) {
                queue.offer(1);
            }
            System.out.println(name + " is over");
        }
    }

    static class Getter implements Runnable {
        private final Queue<Integer> queue;
        private final String name;

        public Getter(Queue<Integer> queue, String name) {
            this.queue = queue;
            this.name = name;
        }

        @Override
        public void run() {
            int i = 0;
            while (i < TEST_COUNT) {
                synchronized (Getter.class) {
                    if (!queue.isEmpty()) {
                        queue.poll();
                        i ++;
                    }
                }
            }
            System.out.println(name + " is over");
        }
    }
}
